module mxu_instr_mngr (
    input logic                   clk,
    input logic                   rst_n,
          lpu_is_mxu_instr_if.in  lpu_mxu_instr,
          mxu_cfg_if.lmb_out      lmb_rdgen_cfg,
          mxu_cfg_if.rmb_out      rmb_rdgen_cfg,
          mxu_cfg_if.psb_out      psb_rdgen_cfg,
          mxu_cfg_if.pmb_out      pmb_rdgen_cfg,
          mxu_cfg_if.mat_ctrl_out mat_ctrl_cfg
);

    logic [15:0] instr_idx_to_recv;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            instr_idx_to_recv <= 16'd0;
        end else begin
            if (lpu_mxu_instr.vld && lpu_mxu_instr.rdy) begin
                instr_idx_to_recv <= instr_idx_to_recv + 16'd1;
            end
        end
    end

    instr_mxu_matmul_t instr_slt[`MXU_INSTR_PIPE_DEPTH];
    logic [`MXU_INSTR_PIPE_DEPTH-1:0] instr_vld;
    logic [`MXU_CFG_NUM-1:0] cfg_sent[`MXU_INSTR_PIPE_DEPTH];  // 0: lmb, 1: rmb, 2: psb, 3: pmb, 4: mat_ctrl
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            instr_vld     <= 4'b0;
            instr_slot[i] <= 'd0;
        end else begin
            for (integer i = 0; i < `MXU_INSTR_PIPE_DEPTH - 1; i = i + 1) begin
                if (instr_vld[i] == 1'b0) begin
                    if (lpu_mxu_instr.vld && lpu_mxu_instr.rdy && (i == instr_idx_to_recv)) begin
                        instr_slt[i] <= lpu_mxu_instr.pld;
                        instr_vld[i] <= 1'b1;
                    end
                end else begin
                    if (cfg_sent[i] == 4'b1111) begin
                        instr_slot[i] <= 'd0;
                        instr_vld[i]  <= 1'b0;
                    end
                end
            end
        end
    end
    assign lpu_mxu_instr.rdy = ~instr_vld[instr_idx_to_recv];

    logic [$clog2(`MXU_INSTR_PIPE_DEPTH)-1:0]
        instr_idx_to_send_lmb,
        instr_idx_to_send_rmb,
        instr_idx_to_send_psb,
        instr_idx_to_send_pmb,
        instr_idx_to_send_mat_ctrl;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            instr_idx_to_send_lmb      <= 'd0;
            instr_idx_to_send_rmb      <= 'd0;
            instr_idx_to_send_psb      <= 'd0;
            instr_idx_to_send_pmb      <= 'd0;
            instr_idx_to_send_mat_ctrl <= 'd0;
            for (integer i = 0; i < `MXU_INSTR_PIPE_DEPTH; i = i + 1) begin
                cfg_sent[i] <= 'd0;
            end
        end else begin
            // Clear cfg_sent when instruction is completed and invalidated
            for (integer i = 0; i < `MXU_INSTR_PIPE_DEPTH; i = i + 1) begin
                if (cfg_sent[i] == {`MXU_CFG_NUM{1'b1}}) begin
                    cfg_sent[i] <= 'd0;
                end
            end

            if(lmb_rdgen_cfg.rdy && instr_vld[instr_idx_to_send_lmb] && (cfg_sent[instr_idx_to_send_lmb][0] == 1'b0)) begin
                instr_idx_to_send_lmb              <= instr_idx_to_send_lmb + 'd1;
                cfg_sent[instr_idx_to_send_lmb][0] <= 1'b1;
            end
            if(rmb_rdgen_cfg.rdy && instr_vld[instr_idx_to_send_rmb] && (cfg_sent[instr_idx_to_send_rmb][1] == 1'b0)) begin
                instr_idx_to_send_rmb              <= instr_idx_to_send_rmb + 'd1;
                cfg_sent[instr_idx_to_send_rmb][1] <= 1'b1;
            end
            if (instr_slt[instr_idx_to_send_psb].psum_en) begin
                if(psb_rdgen_cfg.rdy && instr_vld[instr_idx_to_send_psb] && (cfg_sent[instr_idx_to_send_psb][2] == 1'b0)) begin
                    instr_idx_to_send_psb              <= instr_idx_to_send_psb + 'd1;
                    cfg_sent[instr_idx_to_send_psb][2] <= 1'b1;
                end
            end else begin
                cfg_sent[instr_idx_to_send_psb][2] <= 1'b1;
            end
            if (instr_slt[instr_idx_to_send_pmb].bias_en) begin
                if(pmb_rdgen_cfg.rdy && instr_vld[instr_idx_to_send_pmb] && (cfg_sent[instr_idx_to_send_pmb][3] == 1'b0)) begin
                    instr_idx_to_send_pmb              <= instr_idx_to_send_pmb + 'd1;
                    cfg_sent[instr_idx_to_send_pmb][3] <= 1'b1;
                end
            end else begin
                cfg_sent[instr_idx_to_send_pmb][3] <= 1'b1;
            end
            if(mat_ctrl_cfg.rdy && instr_vld[instr_idx_to_send_mat_ctrl] && (cfg_sent[instr_idx_to_send_mat_ctrl][4] == 1'b0)) begin
                instr_idx_to_send_mat_ctrl              <= instr_idx_to_send_mat_ctrl + 'd1;
                cfg_sent[instr_idx_to_send_mat_ctrl][4] <= 1'b1;
            end
        end
    end
    assign lmb_rdgen_cfg.vld = instr_vld[instr_idx_to_send_lmb] && (cfg_sent[instr_idx_to_send_lmb][0] == 1'b0);
    assign lmb_rdgen_cfg.lmb_addr = instr_slt[instr_idx_to_send_lmb].lmb_addr;
    assign lmb_rdgen_cfg.slice_m = instr_slt[instr_idx_to_send_lmb].slice_m;
    assign lmb_rdgen_cfg.slice_n = instr_slt[instr_idx_to_send_lmb].slice_n;
    assign lmb_rdgen_cfg.slice_k1 = instr_slt[instr_idx_to_send_lmb].slice_k1;

    assign rmb_rdgen_cfg.vld = instr_vld[instr_idx_to_send_rmb] && (cfg_sent[instr_idx_to_send_rmb][1] == 1'b0);
    assign rmb_rdgen_cfg.rmb_addr = instr_slt[instr_idx_to_send_rmb].rmb_addr;
    assign rmb_rdgen_cfg.slice_m = instr_slt[instr_idx_to_send_rmb].slice_m;
    assign rmb_rdgen_cfg.slice_n = instr_slt[instr_idx_to_send_rmb].slice_n;
    assign rmb_rdgen_cfg.slice_k1 = instr_slt[instr_idx_to_send_rmb].slice_k1;

    assign psb_rdgen_cfg.vld = instr_slt[instr_idx_to_send_psb].psum_en && instr_vld[instr_idx_to_send_psb] && (cfg_sent[instr_idx_to_send_psb][2] == 1'b0);
    assign psb_rdgen_cfg.psb_addr = instr_slt[instr_idx_to_send_psb].psb_addr;
    assign psb_rdgen_cfg.slice_n = instr_slt[instr_idx_to_send_psb].slice_n;
    assign psb_rdgen_cfg.slice_k1 = instr_slt[instr_idx_to_send_psb].slice_k1;
    assign psb_rdgen_cfg.slice_m = instr_slt[instr_idx_to_send_psb].slice_m;

    assign pmb_rdgen_cfg.vld = instr_slt[instr_idx_to_send_pmb].bias_en && instr_vld[instr_idx_to_send_pmb] && (cfg_sent[instr_idx_to_send_pmb][3] == 1'b0);
    assign pmb_rdgen_cfg.pmb_addr = instr_slt[instr_idx_to_send_pmb].pmb_addr;
    assign pmb_rdgen_cfg.slice_n = instr_slt[instr_idx_to_send_pmb].slice_n;
    assign pmb_rdgen_cfg.slice_k1 = instr_slt[instr_idx_to_send_pmb].slice_k1;
    assign pmb_rdgen_cfg.slice_m = instr_slt[instr_idx_to_send_pmb].slice_m;

    assign mat_ctrl_cfg.vld = instr_vld[instr_idx_to_send_mat_ctrl] && (cfg_sent[instr_idx_to_send_mat_ctrl][4] == 1'b0);
    assign mat_ctrl_cfg.slice_m = instr_slt[instr_idx_to_send_mat_ctrl].slice_m;
    assign mat_ctrl_cfg.slice_n = instr_slt[instr_idx_to_send_mat_ctrl].slice_n;
    assign mat_ctrl_cfg.slice_k1 = instr_slt[instr_idx_to_send_mat_ctrl].slice_k1;

endmodule
